package top.malaoshi.data;

import top.malaoshi.util.StringUtil;

import java.util.*;

/**
 * 查询后，返回 DataRow[] 数组
 */
public class DataQuery {
    /**
     * 查询所有列
     * @return
     */
    public static DataRow[] select(DataTable dataTable,String columnName, Option option, Object value){
        DataColumn dataColumn = dataTable.columns().get(columnName);
        // 如果列类型是int
        if(dataColumn.getType() == DataColumn.Type.UNDEFINED){
            throw new DataColumnTypeMismatchException(columnName,dataColumn.getType(),value);
        }else if(dataColumn.getType() == DataColumn.Type.INT){
            if(value instanceof Integer){ // 如果传入的是 int 类型，直接调用 toIntAndFilter()
                return toIntAndFilter(dataTable,columnName, option, (Integer)value);
            }else if(value instanceof Double){ // 如果传入的是 double类型，调用 toDoubleAndFilter()
                return toDoubleAndFilter(dataTable,columnName,option,(Double)value);
            }else if(value instanceof String){ // 如果传入的是 string 类型，调用 toIntAndFilter
                return toIntAndFilter(dataTable,columnName,option,Integer.parseInt((String)value));
            }else{
                throw new DataColumnTypeMismatchException(columnName,dataColumn.getType(),value);
            }
        }else if(dataColumn.getType() == DataColumn.Type.DOUBLE){ // 如果列类型是 double
            if(value instanceof Integer){ // 如果传入的是 int 类型，调用 toDoubleAndFilter ()
                return toDoubleAndFilter(dataTable,columnName, option,Double.parseDouble(value.toString()));
            }else if(value instanceof Double){ // 如果传入的是 double类型，调用 toDoubleAndFilter()
                return toDoubleAndFilter(dataTable,columnName,option,(Double)value);
            }else if(value instanceof String){ // 如果传入的是 string 类型，调用 filterInt
                return toDoubleAndFilter(dataTable,columnName,option,Double.parseDouble((String)value));
            }else{
                throw new DataColumnTypeMismatchException(columnName,dataColumn.getType(),value);
            }
        }else if(dataColumn.getType() == DataColumn.Type.TEXT){ // 如果列类型是字符串
            if(value instanceof Integer){ // 如果传入的是 int 类型，调用 toStringAndFilter ()
                return toStringAndFilter(dataTable,columnName, option,value.toString());
            }else if(value instanceof Double){ // 如果传入的是 double类型，调用 toStringAndFilter()
                return toStringAndFilter(dataTable,columnName,option,value.toString());
            }else if(value instanceof String){ // 如果传入的是 string 类型，调用 toStringAndFilter
                return toStringAndFilter(dataTable,columnName,option,(String)value);
            }else{
                throw new DataColumnTypeMismatchException(columnName,dataColumn.getType(),value);
            }
        }
        return null;
    }


    /**
     * 将列值转成 int 类型再比较
     * @param columnName
     * @param option
     * @param value
     * @return
     */
    private static DataRow[] toIntAndFilter(DataTable dataTable,String columnName, Option option, int value){
        if( option == Option.gt     ||
                option == Option.gte    ||
                option == Option.lt     ||
                option == Option.lte    ||
                option == Option.eq     ||
                option == Option.neq){
        }else{
            throw new DataQueryNotOptionException(option,value);
        }
        // 先从索引查询
        DataRow[] res = filterIndex(dataTable, columnName, option, value);
        if(res != null){
            return res;
        }

        List<DataRow> dataRows = new ArrayList<>();

        int columnsIndex = dataTable.columns().getIndex(columnName);
        for(DataRow row: dataTable.rows().rowList){
            Integer v = row.getIntValue(columnsIndex);// 取出数据

            if (    (option == Option.gt && v > value) ||
                    (option == Option.gte && v >= value) ||
                    (option == Option.lt && v < value )||
                    (option == Option.lte && v <= value )||
                    (option == Option.eq && v == value )||
                    (option == Option.neq && v != value )
            ) {
                dataRows.add(row);
            }

        }
        DataRow[] arr = new DataRow[dataRows.size()];
        return dataRows.toArray(arr);
    }


    /**
     * 将列值转成 double 类型再比较
     * @param columnName
     * @param option
     * @param value
     * @return
     */
    private static DataRow[] toDoubleAndFilter(DataTable dataTable,String columnName, Option option, double value){
        if( option == Option.gt     ||
                option == Option.gte    ||
                option == Option.lt     ||
                option == Option.lte    ||
                option == Option.eq     ||
                option == Option.neq){

        }else{
            throw new DataQueryNotOptionException(option,value);
        }
        // 先从索引查询
        DataRow[] res = filterIndex(dataTable, columnName, option, value);
        if(res != null){
            return res;
        }

        List<DataRow> dataRows = new ArrayList<>();
        int columnsIndex = dataTable.columns().getIndex(columnName);
        for(DataRow row: dataTable.rows().rowList){
            Double v = row.getDoubleValue(columnsIndex);// 取出数据

            if (    (option == Option.gt && v > value) ||
                    (option == Option.gte && v >= value) ||
                    (option == Option.lt && v < value )||
                    (option == Option.lte && v <= value )||
                    (option == Option.eq && v == value )||
                    (option == Option.neq && v != value )
            ) {
                dataRows.add(row);
            }

        }

        DataRow[] arr = new DataRow[dataRows.size()];
        return dataRows.toArray(arr);
    }

    /**
     * 将列值转成 string 类型再比较
     * @param columnName
     * @param option
     * @param value
     * @return
     */
    private static DataRow[] toStringAndFilter(DataTable dataTable,String columnName, Option option, String value){
        // 先从索引查询
        DataRow[] res = filterIndex(dataTable, columnName, option, value);
        if(res != null){
            return res;
        }

        List<DataRow> dataRows = new ArrayList<>();
        int columnsIndex = dataTable.columns().getIndex(columnName);
        for(DataRow row: dataTable.rows().rowList){

            String v = row.getStringValue(columnsIndex);// 取出数据
            if (    (option == Option.contain && v.contains(value))     ||
                    (option == Option.containsIgnoreCase && StringUtil.containsIgnoreCase(v,value))   ||
                    (option == Option.start && v.startsWith(value))     ||
                    (option == Option.startsIgnoreCase && StringUtil.startsWithIgnoreCase(v,value))   ||
                    (option == Option.end && v.endsWith(value) )        ||
                    (option == Option.endsIgnoreCase && StringUtil.endsWithIgnoreCase(v,value) )      ||
                    (option == Option.like && StringUtil.like(v,value,false) )              ||
                    (option == Option.likeIgnoreCase && StringUtil.like(v,value,true) )     ||
                    (option == Option.eq && v.equals(value) )           ||
                    (option == Option.neq && !v.equals(value))          ||
                    (option == Option.lt && v.compareTo(value) < 0)     ||
                    (option == Option.lte && v.compareTo(value) <= 0)   ||
                    (option == Option.gt && v.compareTo(value) > 0)     ||
                    (option == Option.gte && v.compareTo(value) >= 0)

            ) {
                dataRows.add(row);
            }
        }
        DataRow[] arr = new DataRow[dataRows.size()];
        return dataRows.toArray(arr);
    }


    /**
     * 从索引查询，不是索引列返回 null
     * 目前只支持 Option.eq 查询
     * @param dataTable
     * @param columnName
     * @param option
     * @param value
     * @return
     */
    private static DataRow[] filterIndex(DataTable dataTable, String columnName, Option option, Number value){
        // 判断是否索引列，不是索引列返回null
        boolean b = dataTable.indexManager.isIndex(columnName);
        if(!b){
            return null;
        }
        // 判断条件
        if( option == Option.gt     ||
            option == Option.gte    ||
            option == Option.lt     ||
            option == Option.lte    ||
            option == Option.eq     ){
        }else if(option == Option.neq){ // 索引不支持 != 查询
            return null;
        }else{ // 其他条件均报错
            throw new DataQueryNotOptionException(option,value);
        }

        List<Integer> rowNumList = new ArrayList<>();
        if ( option == Option.gt ){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.tailMap(value, false);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.gte){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.tailMap(value, true);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.lt){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.headMap(value, false);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.lte){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.headMap(value, true);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.eq){
            List<Integer> rowNumListTemp = dataTable.indexManager.getRowNumByIndex(columnName, value);
            if(rowNumListTemp!=null && !rowNumListTemp.isEmpty()) {
                rowNumList.addAll(rowNumListTemp);
            }
        }

        List<DataRow> dataRows = new ArrayList<>();
        for( int rowNum  : rowNumList){
            DataRow dataRow = dataTable.rows().get(rowNum);
            dataRows.add(dataRow);
        }
        DataRow[] arr = new DataRow[dataRows.size()];
        return dataRows.toArray(arr);
    }

    private static void putNavigableMap2RowNumList(NavigableMap<Object, List<Integer>> map,List<Integer> rowNumList){

        for ( Map.Entry<Object, List<Integer>> item : map.entrySet()){
            List<Integer> rowNumListTemp = item.getValue();
            rowNumList.addAll(rowNumListTemp);
        }
    }

    /**
     * 先从索引开始查询
     * 目前支持 Option.start、Option.eq、lt、lte、gt、gte 查询
     * @param columnName
     * @param option
     * @param value
     * @return
     */
    private static DataRow[] filterIndex(DataTable dataTable, String columnName, Option option, String value){
        // 判断是否索引列，不是索引列返回null
        boolean b = dataTable.indexManager.isIndex(columnName);
        if(!b){
            return null;
        }
        if (    option == Option.start      ||
                option == Option.eq         ||
                option == Option.lt         ||
                option == Option.lte        ||
                option == Option.gt         ||
                option == Option.gte

        ) {

        }else{ // 索引不支持 !=、end、忽略大小写查询
            return null;
        }


        List<Integer> rowNumList = new ArrayList<>();


        if(option == Option.start){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.tailMap(value, true);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.eq ){
            // 如果是索引列，就取出符合该索引的行号
            List<Integer> rowNumListTemp = dataTable.indexManager.getRowNumByIndex(columnName, value);
            if(rowNumListTemp!=null && !rowNumListTemp.isEmpty()) {
                rowNumList.addAll(rowNumListTemp);
            }
        }else if(option == Option.lt ){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.headMap(value, false);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.lte ){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.headMap(value, true);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.gt ){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.tailMap(value, false);
            putNavigableMap2RowNumList(map,rowNumList);
        }else if(option == Option.gte ){
            TreeMap<Object, List<Integer>> t = dataTable.indexManager.getIndexAndRowNum(columnName);
            NavigableMap<Object, List<Integer>> map = t.tailMap(value, true);
            putNavigableMap2RowNumList(map,rowNumList);
        }
        List<DataRow> dataRows = new ArrayList<>();
        for( int rowNum  : rowNumList){
            DataRow dataRow = dataTable.rows().get(rowNum);
            dataRows.add(dataRow);
        }
        DataRow[] arr = new DataRow[dataRows.size()];
        return dataRows.toArray(arr);
    }

    /**
     * 查询操作
     */
    public enum Option{
        /**
         * 大于，适用integer、double、String类型
         */
        gt,
        /**
         * 大于等于，适用integer、double、String类型
         */
        gte,
        /**
         * 小于，适用integer、double、String类型
         */
        lt,
        /**
         * 小于等于，适用integer、double、String类型
         */
        lte,
        /**
         * 等于，适用integer、double、String类型
         */
        eq,
        /**
         * 不等于，适用integer、double、String类型
         */
        neq,
        /**
         * 包含，适用string类型
         */
        contain,
        /**
         * 忽略大小写包含，适用string类型
         */
        containsIgnoreCase,
        /**
         * 开头能够匹配，适用string类型
         */
        start,
        /**
         * 开头能够匹配（忽略大小写），适用string类型
         */
        startsIgnoreCase,
        /**
         * 结尾能够匹配，适用string类型
         */
        end,
        /**
         * 结尾能够匹配（忽略大小写），适用string类型
         */
        endsIgnoreCase,
        /**
         * 模糊查询，支持 %和_，区分大小写
         */
        like,
        /**
         * 模糊查询，支持 %和_，不区分大小写
         */
        likeIgnoreCase,
    }
}
